import tkinter as tk  # 导入 tkinter 库，用于创建 GUI 界面，并简称为 tk
import tkinter.messagebox  # 导入 tkinter 的 messagebox 模块，用于显示消息框
import pickle  # 导入 pickle 模块，用于序列化和反序列化 Python 对象结构
import random  # 导入 random 模块，用于生成随机数
from PIL import ImageTk  # 从 PIL (Pillow) 库中导入 ImageTk 模块，用于在 Tkinter 中显示图片


def 生成验证码():
    """生成一个六位数的随机验证码，包含大小写字母和数字。"""
    验证码字符列表 = ''  # 初始化一个空字符串，用于存储生成的验证码
    # 每一位验证码都有三种可能（大写字母，小写字母，数字）
    for _ in range(6):  # 循环生成六位验证码
        类型选择 = random.randint(1, 3)  # 随机选择验证码字符的类型：1-大写字母, 2-小写字母, 3-数字
        if 类型选择 == 1:  # 如果选择类型1
            大写字母ASCII = random.randint(65, 90)  # 生成一个大写字母的 ASCII 码 (A-Z)
            随机大写字母 = chr(大写字母ASCII)  # 将 ASCII 码转换为对应的字符
            验证码字符列表 = 验证码字符列表 + 随机大写字母  # 将生成的字符追加到验证码字符串

        elif 类型选择 == 2:  # 如果选择类型2
            小写字母ASCII = random.randint(97, 122)  # 生成一个小写字母的 ASCII 码 (a-z)
            随机小写字母 = chr(小写字母ASCII)  # 将 ASCII 码转换为对应的字符
            验证码字符列表 = 验证码字符列表 + 随机小写字母  # 将生成的字符追加到验证码字符串
        elif 类型选择 == 3:  # 如果选择类型3
            随机数字 = random.randint(0, 9)  # 生成一个 0-9 之间的随机数字
            验证码字符列表 = 验证码字符列表 + str(随机数字)  # 将数字转换为字符串并追加到验证码字符串

    return 验证码字符列表  # 返回生成的六位验证码字符串


# 全局变量，存储当前生成的验证码值
当前验证码 = 生成验证码()  # 调用函数生成一个验证码，并存储在全局变量中

# --- 主窗口设置 ---
主窗口 = tk.Tk()  # 创建 Tkinter 主窗口实例
主窗口.title('图书管理系统')  # 设置主窗口的标题
主窗口.geometry('450x300')  # 设置主窗口的尺寸（宽度x高度）
主窗口.resizable(width=False, height=False)  # 设置主窗口大小不可调整

# --- 背景图片设置 ---
画布 = tk.Canvas(主窗口, height=300, width=500)  # 在主窗口上创建一个画布，用于放置图片
try:
    背景图片文件 = ImageTk.PhotoImage(file=r'背景.png')  # 加载背景图片 (请确保图片文件存在于正确路径)
    画布.create_image(0, 0, anchor='nw', image=背景图片文件)  # 在画布上(0,0)位置按西北角锚点放置图片
    画布.pack(side='top')  # 将画布放置在主窗口的顶部
except tk.TclError: # 文件未找到或其他Pillow错误
    print("错误：无法加载背景图片 '背景.png'。请确保文件存在且路径正确。") # 打印错误信息
    # 可以选择在这里放置一个默认背景或者不设置背景

# --- 标签和输入框 --- #
# 用户名标签和输入框
tk.Label(主窗口, text='用户名:').place(x=100, y=110)  # 创建“用户名”标签并放置在指定位置
用户名输入变量 = tk.StringVar()  # 创建一个 StringVar 对象，用于存储用户名输入框的内容
用户名输入框 = tk.Entry(主窗口, textvariable=用户名输入变量)  # 创建用户名输入框，并将其与 用户名输入变量 关联
用户名输入框.place(x=160, y=110)  # 放置用户名输入框

# 密码标签和输入框
tk.Label(主窗口, text='密   码:').place(x=100, y=150)  # 创建“密码”标签并放置在指定位置
密码输入变量 = tk.StringVar()  # 创建一个 StringVar 对象，用于存储密码输入框的内容
密码输入框 = tk.Entry(主窗口, textvariable=密码输入变量, show='*')  # 创建密码输入框，设置 show='*' 使输入内容显示为星号
密码输入框.place(x=160, y=150)  # 放置密码输入框

# 验证码标签和输入框
tk.Label(主窗口, text='验证码:').place(x=100, y=190)  # 创建“验证码”标签并放置在指定位置
验证码标签 = tk.Label(主窗口, text=当前验证码) # 创建显示当前验证码的标签
验证码标签.place(x=310, y=190) # 放置验证码标签
验证码输入变量 = tk.StringVar()  # 创建一个 StringVar 对象，用于存储验证码输入框的内容
验证码输入框 = tk.Entry(主窗口, textvariable=验证码输入变量)  # 创建验证码输入框
验证码输入框.place(x=160, y=190)  # 放置验证码输入框

print(f"当前验证码: {当前验证码}")  # 在控制台打印当前验证码，方便调试

# --- 用户信息存储文件名 ---
用户信息文件 = 'usr_info.pickle'  # 定义存储用户信息的 pickle 文件名


def 用户登录():
    """处理用户登录逻辑。"""
    global 当前验证码 # 声明修改全局变量
    # 获取用户输入
    输入用户名 = 用户名输入变量.get()  # 获取用户名输入框中的内容
    输入密码 = 密码输入变量.get()  # 获取密码输入框中的内容
    输入验证码 = 验证码输入变量.get()  # 获取验证码输入框中的内容

    # 加载用户信息
    try:
        with open(用户信息文件, 'rb') as 文件对象:  # 以二进制读取模式打开用户信息文件
            用户信息字典 = pickle.load(文件对象)  # 从文件中加载用户信息字典
    except FileNotFoundError:  # 如果文件未找到
        with open(用户信息文件, 'wb') as 文件对象:  # 以二进制写入模式创建新的用户信息文件
            用户信息字典 = {'admin': 'admin'}  # 创建一个包含默认管理员账户的字典
            pickle.dump(用户信息字典, 文件对象)  # 将默认用户信息写入文件

    # 校验登录信息
    if not 输入用户名 or not 输入密码:  # 判断用户名或密码是否为空
        tkinter.messagebox.showerror(title='错误', message='用户名或密码不能为空')  # 显示错误消息框
        return  # 结束登录函数

    if 输入用户名 in 用户信息字典:  # 判断输入的用户名是否存在于用户信息中
        if 输入密码 == 用户信息字典[输入用户名]:  # 判断输入的密码是否与存储的密码匹配
            if 输入验证码 == 当前验证码:  # 判断输入的验证码是否与当前验证码匹配
                tkinter.messagebox.showinfo(title='欢迎', message=f'欢迎您：{输入用户名}')  # 显示欢迎消息框
                # 可以在这里添加登录成功后的操作，例如打开新的窗口
            else:
                tkinter.messagebox.showerror(title='错误', message='验证码错误')  # 显示验证码错误消息框
                # 刷新验证码
                # global 当前验证码 # 声明修改全局变量 # <--- 此行已移至函数开头
                当前验证码 = 生成验证码() # 重新生成验证码
                验证码标签.config(text=当前验证码) # 更新验证码标签的文本
        else:
            tkinter.messagebox.showerror(title='错误', message='密码错误')  # 显示密码错误消息框
    else:
        是否注册 = tkinter.messagebox.askyesno('提示', '您还没有注册，是否现在注册？')  # 弹出是否注册的询问框
        if 是否注册:  # 如果用户选择“是”
            用户注册()  # 调用用户注册函数


def 用户注册():
    """处理用户注册逻辑，并打开注册窗口。"""

    def 执行注册():
        """在注册窗口中点击“确认注册”按钮时执行的函数。"""
        # 获取注册窗口中的输入
        新用户名 = 新用户名输入变量.get()  # 获取新用户名输入框的内容
        新密码 = 新密码输入变量.get()  # 获取新密码输入框的内容
        确认密码 = 确认密码输入变量.get()  # 获取确认密码输入框的内容

        # 加载已有的用户信息
        try:
            with open(用户信息文件, 'rb') as 文件对象:  # 以二进制读取模式打开用户信息文件
                已存在用户信息 = pickle.load(文件对象)  # 加载已有的用户信息
        except FileNotFoundError:  # 如果文件未找到
            已存在用户信息 = {}  # 初始化一个空的用户信息字典

        # 校验注册信息
        if not 新用户名 or not 新密码:  # 判断新用户名或新密码是否为空
            tkinter.messagebox.showerror('错误', '用户名或密码不能为空', parent=注册窗口)  # 显示错误消息框，parent指定父窗口
            return # 结束函数
        if 新用户名 in 已存在用户信息:  # 判断新用户名是否已存在
            tkinter.messagebox.showerror('错误', '用户名已存在', parent=注册窗口)  # 显示错误消息框
        elif 新密码 != 确认密码:  # 判断两次输入的密码是否一致
            tkinter.messagebox.showerror('错误', '两次输入的密码不一致', parent=注册窗口)  # 显示错误消息框
        else:
            已存在用户信息[新用户名] = 新密码  # 将新的用户名和密码添加到用户信息字典中
            with open(用户信息文件, 'wb') as 文件对象:  # 以二进制写入模式打开用户信息文件
                pickle.dump(已存在用户信息, 文件对象)  # 将更新后的用户信息写入文件
            tkinter.messagebox.showinfo('成功', '注册成功！', parent=注册窗口)  # 显示注册成功消息框
            注册窗口.destroy()  # 关闭注册窗口

    # --- 注册窗口设置 ---
    注册窗口 = tk.Toplevel(主窗口)  # 创建一个新的顶级窗口作为注册界面，父窗口为主窗口
    注册窗口.geometry('350x200')  # 设置注册窗口的尺寸
    注册窗口.title('用户注册')  # 设置注册窗口的标题
    注册窗口.resizable(width=False, height=False) # 设置注册窗口大小不可调整

    # --- 注册窗口的标签和输入框 ---
    # 新用户名
    新用户名输入变量 = tk.StringVar()  # 创建 StringVar 用于存储新用户名
    tk.Label(注册窗口, text='用户名：').place(x=30, y=20)  # “用户名”标签
    tk.Entry(注册窗口, textvariable=新用户名输入变量).place(x=130, y=20)  # 新用户名输入框

    # 新密码
    新密码输入变量 = tk.StringVar()  # 创建 StringVar 用于存储新密码
    tk.Label(注册窗口, text='输入密码：').place(x=30, y=60)  # “输入密码”标签
    tk.Entry(注册窗口, textvariable=新密码输入变量, show='*').place(x=130, y=60)  # 新密码输入框

    # 确认密码
    确认密码输入变量 = tk.StringVar()  # 创建 StringVar 用于存储确认密码
    tk.Label(注册窗口, text='确认密码：').place(x=30, y=100)  # “确认密码”标签
    tk.Entry(注册窗口, textvariable=确认密码输入变量, show='*').place(x=130, y=100)  # 确认密码输入框

    # 确认注册按钮
    确认注册按钮 = tk.Button(注册窗口, text='确认注册', command=执行注册)  # 创建“确认注册”按钮，点击时调用 执行注册 函数
    确认注册按钮.place(x=150, y=140)  # 放置按钮

    注册窗口.grab_set() # 使注册窗口成为模态对话框，阻止与其他窗口交互直到此窗口关闭
    注册窗口.wait_window() # 等待注册窗口关闭

def 用户退出():
    """关闭主应用程序窗口。"""
    主窗口.destroy()  # 销毁主窗口，退出程序


# --- 主窗口按钮 ---
登录按钮 = tk.Button(主窗口, text='登录', command=用户登录)  # 创建“登录”按钮，点击时调用 用户登录 函数
登录按钮.place(x=140, y=230)  # 放置登录按钮

注册按钮 = tk.Button(主窗口, text='注册', command=用户注册)  # 创建“注册”按钮，点击时调用 用户注册 函数
注册按钮.place(x=210, y=230)  # 放置注册按钮

退出按钮 = tk.Button(主窗口, text='退出', command=用户退出)  # 创建“退出”按钮，点击时调用 用户退出 函数
退出按钮.place(x=280, y=230)  # 放置退出按钮

# --- 启动 Tkinter 事件循环 ---
主窗口.mainloop()  # 进入 Tkinter 的事件循环，等待用户操作并响应
